//
//  Swiftris.swift
//  TetrisGame
//
//  Created by mac on 2020/11/23.
//


/// Swiftris Class
class Swiftris {
    var blockArray:Array2D<Block>
    var nextShape:Shape?
    var fallingShape:Shape?
    var delegate:SwiftrisDelegate?
    
    
    /** 游戏分数 */
    private lazy var score = 0
    
    /** 游戏级别 */
    private lazy var level = 1

    init() {
        fallingShape = nil
        nextShape = nil
        blockArray = Array2D<Block>(columns: NumColumns, rows: NumRows)
    }
}


//MARK: -
extension Swiftris {
    
    /** 开始游戏 */
    func beginGame() {
        if (nextShape == nil) {
            nextShape = Shape.random(startingColumn: PreviewColumn, startingRow: PreviewRow)
        }
        delegate?.gameDidBegin(swiftris: self)
    }

    /** 生成方块 */
    func newShape() -> (fallingShape:Shape?, nextShape:Shape?) {
        fallingShape = nextShape
        nextShape = Shape.random(startingColumn: PreviewColumn, startingRow: PreviewRow)
        fallingShape?.moveTo(column: StartingColumn, row: StartingRow)
        
        guard detectIllegalPlacement() == false else {
            nextShape = fallingShape
            nextShape!.moveTo(column: PreviewColumn, row: PreviewRow)
            endGame()
            return (nil, nil)
        }
        
        return (fallingShape, nextShape)
    }
    
    
    /// 检测非法放置(true:非法)
    /// - Returns: Bool
    private func detectIllegalPlacement() -> Bool {
         guard let shape = fallingShape else {
             return false
         }
        
         for block in shape.blocks {
             if block.column < 0 || block.column >= NumColumns
                 || block.row < 0 || block.row >= NumRows {
                 return true
             }
             else if blockArray[block.column, block.row] != nil {
                 return true
             }
         }
        
         return false
    }
    
    func settleShape() {
         guard let shape = fallingShape else {
             return
         }
         for block in shape.blocks {
             blockArray[block.column, block.row] = block
         }
         
         fallingShape = nil
         delegate?.gameShapeDidLand(swiftris: self)
    }

    func detectTouch() -> Bool {
        guard let shape = fallingShape else {
            return false
        }
         
        for bottomBlock in shape.bottomBlocks {
             if bottomBlock.row == NumRows - 1
                 || blockArray[bottomBlock.column, bottomBlock.row + 1] != nil {
                     return true
             }
         }
         
        return false
    }

    
    /// 游戏结束
    func endGame() {
        score = 0
        level = 1
        
        delegate?.gameDidEnd(swiftris: self)
    }

    func dropShape() {
         guard let shape = fallingShape else {
             return
         }
        
         while detectIllegalPlacement() == false {
             shape.lowerShapeByOneRow()
         }
        
         shape.raiseShapeByOneRow()
         delegate?.gameShapeDidDrop(swiftris: self)
     }

    func letShapeFall() {
         guard let shape = fallingShape else {
             return
         }
        
         shape.lowerShapeByOneRow()
         if detectIllegalPlacement() {
             shape.raiseShapeByOneRow()
             if detectIllegalPlacement() {
                 endGame()
             } else {
                 settleShape()
             }
         }
         else {
            delegate?.gameShapeDidMove(swiftris: self)
            if detectTouch() {
                 settleShape()
            }
         }
     }

    func rotateShape() {
         guard let shape = fallingShape else {
             return
         }
        
         shape.rotateClockwise()
         guard detectIllegalPlacement() == false else {
             shape.rotateCounterClockwise()
             return
         }
        
         delegate?.gameShapeDidMove(swiftris: self)
     }

    func moveShapeLeft() {
         guard let shape = fallingShape else {
             return
         }
        
         shape.shiftLeftByOneColumn()
         guard detectIllegalPlacement() == false else {
             shape.shiftRightByOneColumn()
             return
         }
        
         delegate?.gameShapeDidMove(swiftris: self)
     }

    func moveShapeRight() {
         guard let shape = fallingShape else {
             return
         }
        
         shape.shiftRightByOneColumn()
         guard detectIllegalPlacement() == false else {
             shape.shiftLeftByOneColumn()
             return
         }
        
         delegate?.gameShapeDidMove(swiftris: self)
     }
    
    func removeCompletedLines() -> (linesRemoved: Array<Array<Block>>, fallenBlocks: Array<Array<Block>>) {
        var removedLines = Array<Array<Block>>()
        for row in (1..<NumRows).reversed() {
             var rowOfBlocks = Array<Block>()

             for column in 0..<NumColumns {
                 guard let block = blockArray[column, row] else {
                     continue
                 }
                 rowOfBlocks.append(block)
             }
             if rowOfBlocks.count == NumColumns {
                 removedLines.append(rowOfBlocks)
                 for block in rowOfBlocks {
                     blockArray[block.column, block.row] = nil
                 }
             }
         }

        if removedLines.count == 0 {
            return ([], [])
        }
    
        let pointsEarned = removedLines.count * PointsPerLine * level
        score += pointsEarned
        if score >= level * LevelThreshold {
             level += 1
             delegate?.gameDidLevelUp(swiftris: self)
        }

        var fallenBlocks = Array<Array<Block>>()
        for column in 0..<NumColumns {
            var fallenBlocksArray = Array<Block>()

            for row in (1..<removedLines[0][0].row).reversed() {
                guard let block = blockArray[column, row] else {
                    continue
                }
                var newRow = row
                while (newRow < NumRows - 1 && blockArray[column, newRow + 1] == nil) {
                    newRow += 1
                }
                block.row = newRow
                blockArray[column, row] = nil
                blockArray[column, newRow] = block
                fallenBlocksArray.append(block)
            }
        
            if fallenBlocksArray.count > 0 {
                fallenBlocks.append(fallenBlocksArray)
            }
        }
        
        return (removedLines, fallenBlocks)
     }
    
    func removeAllBlocks() -> Array<Array<Block>> {
        var allBlocks = Array<Array<Block>>()
        for row in 0..<NumRows {
            var rowOfBlocks = Array<Block>()
            for column in 0..<NumColumns {
                guard let block = blockArray[column, row] else {
                    continue
                }
                rowOfBlocks.append(block)
                blockArray[column, row] = nil
            }
            allBlocks.append(rowOfBlocks)
        }
        return allBlocks
    }
}


//MARK: - SwiftrisDelegate
protocol SwiftrisDelegate {
    // Invoked when the current round of Swiftris ends
    func gameDidEnd(swiftris: Swiftris)

    // Invoked after a new game has begun
    func gameDidBegin(swiftris: Swiftris)

    // Invoked when the falling shape has become part of the game board
    func gameShapeDidLand(swiftris: Swiftris)

    // Invoked when the falling shape has changed its location
    func gameShapeDidMove(swiftris: Swiftris)

    // Invoked when the falling shape has changed its location after being dropped
    func gameShapeDidDrop(swiftris: Swiftris)

    // Invoked when the game has reached a new level
    func gameDidLevelUp(swiftris: Swiftris)
}
